/*
 * Copyright 2023-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package net.yun789.javasipweb.handler;

import gov.nist.javax.sip.header.WWWAuthenticate;
import lombok.extern.slf4j.Slf4j;
import net.yun789.javasipweb.dto.LoginUserDTO;
import net.yun789.javasipweb.sip.stack.UserTransaction;
import net.yun789.javasipweb.user.context.UserContextRepository;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipProvider;
import javax.sip.address.Address;
import javax.sip.address.URI;
import javax.sip.header.*;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * 注册 请求处理器
 * @author ydy
 */
@Component
@Slf4j
public class RegisterRequestHandler extends AbstractRequestHandler implements RequestHandler{

    @Autowired
    private MessageFactory messageFactory;
    @Autowired
    private  SipProvider sipProvider;
    @Value("${address.outernet}")
    private String addressOuternet;
    @Autowired
    private UserContextRepository userContextRepository;
    @Override
    protected void handleInternal(RequestEvent requestEvent) {
        Request request = requestEvent.getRequest();
        if (null == request)
        {
            log.warn("RegisterRequestHandler request is null.");
            return;
        }
        //System.out.println("Request " + request.toString());
        ServerTransaction serverTransactionId = requestEvent.getServerTransaction();

        try
        {
            Response response = null;
            ToHeader head = (ToHeader)request.getHeader(ToHeader.NAME);
            Address toAddress = head.getAddress();
            URI toURI = toAddress.getURI();
            ContactHeader contactHeader = (ContactHeader) request.getHeader("Contact");
            Address contactAddr = contactHeader.getAddress();
            URI contactURI = contactAddr.getURI();
//            System.out.println("processRegister from: " + toURI + " request str: " + contactURI);
            ExpiresHeader expiresHeader = request.getExpires();
            int expires = expiresHeader==null?0:expiresHeader.getExpires();

            CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);

            // 如果expires不等于0,则为注册，否则为注销。
            if (expires != 0 || contactHeader.getExpires() != 0)
            {
                //获取认证信息
                AuthorizationHeader authorizationHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
                if (authorizationHeader == null) {
                    //401 错误
                    returnNoAuth(request,401);
                    return;
                }else{
                    //验证登录信息
                    boolean loginResult = login(request);
                    if (!loginResult) {
                        return;
                    }




//                    System.out.println("register user " + toURI);
                }

            }
            else
            {
                userContextRepository.logOff(request);
            }

            response = messageFactory.createResponse(200, request);
//            System.out.println("send register response  : " + response.toString());

            if(serverTransactionId == null)
            {
                serverTransactionId = sipProvider.getNewServerTransaction(request);
//
//                UserTransaction userTransaction = new UserTransaction();
//                userTransaction.setBranchId(serverTransactionId.getBranchId());
//                userTransaction.setCallId(((CallIdHeader) request.getHeader(CallIdHeader.NAME)).getCallId());
//                userTransaction.setTransaction(serverTransactionId);
//                userTransactionMap.put(serverTransactionId.getBranchId(),userTransaction);
//
//                synchronized (this){
//                    List<UserTransaction> userTransactionListTemp = userTransactionListMap.computeIfAbsent(serverTransactionId.getBranchId(), k -> new CopyOnWriteArrayList<>());
//                    userTransactionListTemp.add(userTransaction);
//                }

                //serverTransactionId.terminate();
//                System.out.println("register serverTransaction: " + serverTransactionId);
            }
            else
            {
                System.out.println("processRequest serverTransactionId is null.");
            }
            serverTransactionId.sendResponse(response);


        }

        catch (Exception e){
            log.error(ExceptionUtils.getStackTrace(e));
        }

    }


    /**
     * 返回未授权
     * @param request
     * @param statusCode 401 or 407
     */
    private void returnNoAuth(Request request,int statusCode){
        if(statusCode!=401&&statusCode!=407){
            throw  new RuntimeException("statusCode must is  401 or 407");
        }

        try {
            Response response401 = messageFactory.createResponse(statusCode, request);
            WWWAuthenticateHeader wwwAuthenticateHeader=new WWWAuthenticate();
            wwwAuthenticateHeader.setRealm(addressOuternet);
            wwwAuthenticateHeader.setNonce(UUID.randomUUID().toString());
            wwwAuthenticateHeader.setAlgorithm("MD5");
            wwwAuthenticateHeader.setQop("auth");
            response401.addHeader(wwwAuthenticateHeader);
            ServerTransaction newServerTransaction = sipProvider.getNewServerTransaction(request);
            newServerTransaction.sendResponse(response401);
        }catch (Exception e){
            log.error(ExceptionUtils.getStackTrace(e));
            throw  new RuntimeException(e);
        }

    }

    private boolean login(Request request) {

        AuthorizationHeader authorizationHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);

        if(authorizationHeader==null){
            return false;
        }

        FromHeader head = (FromHeader)request.getHeader(FromHeader.NAME);
//        ContactHeader contactHeader = (ContactHeader) request.getHeader(ContactHeader.NAME);
        Address fromAddress = head.getAddress();
        URI fromURI = fromAddress.getURI();
        CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);


        String ha1WaitHexStr = authorizationHeader.getUsername()+":"+ authorizationHeader.getRealm()+":"+"1234";
        String ha1= DigestUtils.md5Hex(ha1WaitHexStr);
//                        String ha2WaitHexStr=Request.REGISTER+":"+head.getAddress().getURI();
        String ha2WaitHexStr=request.getMethod()+":"+ authorizationHeader.getURI().toString();
        String ha2=DigestUtils.md5Hex(ha2WaitHexStr);
        String nonceCount= authorizationHeader.getNonceCount()+"";
        while(nonceCount.length()<8){
            nonceCount="0"+nonceCount;
        }
        String authResponseServeWaitSignStr=ha1 + ":" + authorizationHeader.getNonce()+":"+nonceCount+":"+ authorizationHeader.getCNonce()+":"+ authorizationHeader.getQop()+ ":" + ha2;
        String authResponseServe = DigestUtils.md5Hex(authResponseServeWaitSignStr);
        String authResponseClient = authorizationHeader.getResponse();
        if(!authResponseServe.equals(authResponseClient)){
            returnNoAuth(request,401);
            return false;
        }
        ContactHeader contactHeader = (ContactHeader) request.getHeader(ContactHeader.NAME);

        //添加登陆信息
        LoginUserDTO loginUserDTO = new LoginUserDTO();
        loginUserDTO.setCallId(callIdHeader.getCallId());
        loginUserDTO.setUsername(authorizationHeader.getUsername());
        loginUserDTO.setUri(fromURI);
        loginUserDTO.setRealm(authorizationHeader.getRealm());
        loginUserDTO.setLoginTime(LocalDateTime.now());
        loginUserDTO.setContactURI(contactHeader.getAddress().getURI());
        userContextRepository.saveContext(loginUserDTO,request);
        //CopyOnWriteArrayList
//        List<LoginUserDTO> loginUserDTOList = loginUserDTOListMap.computeIfAbsent(loginUserDTO.getUri().toString(), k -> new CopyOnWriteArrayList<>());

        return true;
    }


    @Override
    public boolean support(String method) {
        return Request.REGISTER.equals(method);
    }

    @Override
    public int getOrder() {
        return Integer.MAX_VALUE;
    }
}
